package com.qf.shopping.utils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

/**
 * @author 三旬
 * @version 2.0  2022/8/29
 */

public class Tools {
    //声明连接对象
    public static Connection conn = null;
    //声明预编译对象
    public static PreparedStatement ps= null;
    //声明结果集对象
    public static ResultSet rs = null;
    //创建加载配置文件的对象
    public static Properties p = new Properties();
    //声明线程本地变量
    static ThreadLocal<Connection> THREADLOCAL= new ThreadLocal<Connection>();

    /**
     * 加载类时加载配置文件
     */
    static{
        //加载连接数据库的配置文件
        loadProPerties("jdbc.properties");
        //加载java实体类学生对象与数据库对象不同的字段
        loadProPerties("orm.properties");
    }

    /**
     * 加载配置文件的方法
     * @param fileName
     */
    public static void loadProPerties(String fileName){
        try {
            p.load(Tools.class.getClassLoader().getResourceAsStream(fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建数据库连接对象的方法
     * @return
     */
    public static Connection getConn(){
        //判断本地线程变量是否为空
        if(THREADLOCAL.get() == null){
            //如果为空则创建一个连接对象
            try {
                Class.forName(p.getProperty("driverClassName"));
                conn = DriverManager.getConnection(p.getProperty("url"),p.getProperty("username"),p.getProperty("password"));
                //将创建的连接对象存放在本地线程变量中
                THREADLOCAL.set(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return THREADLOCAL.get();
    }

    /**
     * 获得插入数据时的主键
     * @param sql
     * @param args
     * @return
     */
    public static int insertGetKey(String sql, Object... args) {
        //1.获得数据库连接对象
        conn = getConn();
        try {
            // 2.执行预处理对象
            // Statement.RETURN_GENERATED_KEYS:插入的时候需要返回主键
            ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            // 3.给占位符赋值
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            // 4.执行sql语句
            int i = ps.executeUpdate(); // 返回是影响的行数
            // 5.获取主键
            ResultSet resultSet = ps.getGeneratedKeys();
            if (resultSet.next()) {
                return resultSet.getInt(1); // 返回才是主键
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return 0;
    }
    /**
     * 对数据库进行更新的操作
     * @param sql
     * @param obj
     * @return
     */
    public static Integer update(String sql, Object ...obj){
        //获得数据库连接对象
        conn = getConn();
        try {
            //执行预编译处理
            ps = conn.prepareStatement(sql);
            //预编译处理对象接收占位符的参数
            for (int i = 0; i < obj.length; i++) {
                ps.setObject(i+1,obj[i]);
            }
            //用预编译对象调用相应的方法，执行sql语句,返回结果
            return ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 数据库的查询操作（返回一条数据）
     * @param sql
     * @param clazz
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> T selectOne(String sql ,Class clazz,Object ...obj ){
        List<T> list = selectList(sql,clazz,obj);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 数据库的查询操作，返回一个集合对象(多条数据)
     * @param sql
     * @param clazz
     * @param obj
     * @param <T>
     * @return
     */
    public static <T>List<T> selectList(String sql, Class<T> clazz, Object ...obj){
        //获得连接数据库对象
        conn = getConn();
        //声明一个集合存放搜索的数据
        List<T> list = new ArrayList<>();
        try {
            //执行预编译处理
            ps = conn.prepareStatement(sql);
            //给占位符赋值
            for(int i = 0; i < obj.length; i++){
                ps.setObject(i+1,obj[i]);
            }
            //执行sql语句，返回一个结果集
            rs = ps.executeQuery();
            //获取反射对象，然后将对象存放进集合中
            while(rs.next()){
                //每行都要实例化一个对象(实例化一个传来的对象)
                T t  =clazz.newInstance();
                //根据该对象获得所有的属性
                //返回一个Field对象数组，该数组指示所有Field(即，它是私有，公共，受保护还是默认)，但不包括继承的字段。
                Field[] f = t.getClass().getDeclaredFields();
                //给该对象的属性赋值
                for (int i = 0; i < f.length; i++) {
                    //获取操作该属性的权限
                    f[i].setAccessible(true);
                    //获得该属性的名字
                    String name = f[i].getName();
                    //接收该属性的值，从当前行的结果集中查找
                    Object value = null;
                    try {
                        value =  rs.getObject(name);
                        //将得到的值按顺序放进当前实例化的对象中
                        f[i].set(t,value);
                    } catch (SQLException e) {
                        //如果找不到行，从配置文件中找
                        String column = p.getProperty(name);
                        if(column != null && !"".equals(column)){
                            value = rs.getObject(column);
                            f[i].set(t,value);
                        }
                    } catch (IllegalArgumentException e) {
                        //将数据库中的集合拆分（数组转集合）
                        String[] array = value.toString().split(",");
                        f[i].set(t, Arrays.asList(array));
                    }
                }
                //将赋完值的对象存进集合中
                list.add(t);
            }
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 关闭连接的方法（动态参数）
     * @param obj
     */
    public static void closeALL(AutoCloseable ...obj) {
        try {
            for (AutoCloseable c : obj) {
                if (c != null) {
                    c.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 事务的开始
     */
    public static void begin(){
        try {
            //获得连接对象，并关闭自动提交事务，改为手动提交事务
            getConn().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 事务的提交
     */
    public static void commit(){
        try {
            conn = getConn();
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            transactionClose(conn,ps,rs);
        }
    }

    /**
     * 事务的回滚
     */
    public static void rollback(){
        try {
            conn = getConn();
            conn.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            transactionClose(conn,ps,rs);
        }
    }

    /**
     * 关闭事务中的连接对象的方法
     */
    public static void transactionClose(Connection conn,PreparedStatement ps,ResultSet rs){
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            THREADLOCAL.remove();
        }
    }
}
